﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using DimensionsHelper.Common.Contracts.DataCollector.Options;
using DimensionsHelper.Common.Utils;

namespace DimensionsHelper.Services.DataCollector;

/// <summary>
///     此接口定义节点类型中可以获取节点路径的接口。
/// </summary>
internal interface ILocatable
{
    /// <summary>
    ///     添加一个新列路径。
    /// </summary>
    /// <param name="key">路径关键字</param>
    /// <param name="value">路径值</param>
    void AddPath(string key, string value);

    /// <summary>
    ///     添加一个路径列表。
    /// </summary>
    /// <param name="pairs">路径关键字-值列表</param>
    void AddPath(IEnumerable<KeyValuePair<string, string>> pairs);

    /// <summary>
    ///     锁定当前路径。
    /// </summary>
    void Lock();

    /// <summary>
    ///     获取全节点路径。
    /// </summary>
    /// <returns>节点全路径，其中Key和Value都可以在节点中检索到</returns>
    IEnumerable<KeyValuePair<string, string>> GetLocation();
}


[DebuggerDisplay("Row = {Row}, Key = {Key}, Value = {Value}")]
internal class TableRow : ILocatable
{
    private readonly Dictionary<string, string> _path;

    public TableRow(string key, string value, int row, bool noValue = false)
    {
        IsLocked = false;
        Key = key;
        Value = value;
        Row = row;
        NoValue = noValue;
        IsHeaderWritten = false;
        _path = new Dictionary<string, string>();
        AddPath(key, value);
    }

    /// <summary>
    ///     当前行路径是否已经锁定。锁定后调用<see cref="AddPath(string,string)" />后将会报错。
    /// </summary>
    private bool IsLocked { get; set; }

    /// <summary>
    ///     行使用的类型名。
    /// </summary>
    public string Key { get; set; }

    /// <summary>
    ///     行使用的类型值。
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    ///     行号。
    /// </summary>
    public int Row { get; set; }

    /// <summary>
    ///     改行是否不保存数据。
    /// </summary>
    public bool NoValue { get; set; }

    /// <summary>
    ///     此行的行头是否已经被填充了。
    ///     如果此行的行头需要依据节点值进行填充，在初始化此行时，
    ///     不要将此属性设置成 true。
    /// </summary>
    public bool IsHeaderWritten { get; set; }

    /// <summary>
    ///     添加新行路径。
    /// </summary>
    public void AddPath(string typeName, string typeValue)
    {
        if (IsLocked)
        {
            throw new Exception("行已锁定，无法修改路径。");
        }

        _path.TryAdd(typeName, typeValue);
    }

    /// <summary>
    ///     <inheritdoc cref="AddPath(string, string)" />
    /// </summary>
    public void AddPath(IEnumerable<KeyValuePair<string, string>> path)
    {
        foreach (var pair in path)
        {
            AddPath(pair.Key, pair.Value);
        }
    }

    public void Lock()
    {
        IsLocked = true;
    }


    public IEnumerable<KeyValuePair<string, string>> GetLocation()
    {
        return _path;
    }

    /// <summary>
    ///     测试给定节点路径是否符合当前行路径。
    /// </summary>
    public bool TestNodePath(IEnumerable<ExcelDataNodePathElement> nodePath)
    {
        // TableRow.path 和 nodePath 的长度一般是不一样的，
        // 此处比较 TableRow.path 中的内容是否都在 nodePath 中。
        return _path.All(item => nodePath.FirstOrDefault(e => e.Type == item.Key && e.Name == item.Value) != null);
    }
}


[DebuggerDisplay("Key = {Key}, Value = {Value}")]
internal class TableColumn : ILocatable
{
    private readonly Dictionary<object, int> _gapColumns;
    private readonly Dictionary<int, int> _indexColumns;

    private readonly Dictionary<string, string> _path;

    public TableColumn(string key, string value)
    {
        _path = new Dictionary<string, string>();
        _indexColumns = new Dictionary<int, int>();
        _gapColumns = new Dictionary<object, int>();
        Key = key;
        Value = value;

        AddPath(key, value);
    }

    /// <summary>
    ///     选取Gap配置关键字的函数。如果
    /// </summary>
    public Func<Gap, object>? GapKeySelector { get; set; }

    /// <summary>
    ///     列关键字
    /// </summary>
    public string Key { get; }

    /// <summary>
    ///     关键字对应的值
    /// </summary>
    public string Value { get; }

    /// <summary>
    ///     如果当前列用于计算Gap，返回true。
    /// </summary>
    public bool HasGap => _gapColumns.Count > 0;

    /// <summary>
    ///     当前列路径是否已经锁定。锁定后调用<see cref="AddPath(string,string)" />后将会报错。
    /// </summary>
    private bool IsLocked { get; set; }

    public void Lock()
    {
        IsLocked = true;
    }

    public void AddPath(string type, string value)
    {
        if (IsLocked)
        {
            throw new Exception("列已锁定，无法修改路径。");
        }

        _path.TryAdd(type, value);
    }

    public void AddPath(IEnumerable<KeyValuePair<string, string>> path)
    {
        foreach (var pair in path)
        {
            AddPath(pair.Key, pair.Value);
        }
    }

    public IEnumerable<KeyValuePair<string, string>> GetLocation()
    {
        return _path;
    }

    private object SelectGapKey(Gap gap)
    {
        return GapKeySelector != null ? GapKeySelector.Invoke(gap) : gap;
    }

    /// <summary>
    ///     设置指定索引的列号
    /// </summary>
    public void SetIndexColumn(int index, int column)
    {
        _indexColumns.SetValue(index, column);
    }

    /// <summary>
    ///     获取指定索引的列号，如果索引不存在，返回-1
    /// </summary>
    public int GetIndexColumn(int index)
    {
#if NET8_0_OR_GREATER
            return _indexColumns.GetValueOrDefault(index, -1);
#else
        return _indexColumns.TryGetValue(index, out var result) ? result : -1;
#endif
    }

    /// <summary>
    ///     添加列使用的Gap。
    /// </summary>
    /// <param name="gap">Gap定义</param>
    /// <param name="column">列号</param>
    public bool TrySetGapColumn(Gap gap, int column)
    {
        var key = SelectGapKey(gap);
        return _gapColumns.TryAdd(key, column);
    }

    /// <summary>
    ///     获取给定Gap对应的列号。
    /// </summary>
    /// <param name="gap">查找的Gap对象</param>
    /// <returns>Gap所在列，如果未找到，返回-1</returns>
    public int GetGapColumn(Gap gap)
    {
#if NET8_0_OR_GREATER
            return _gapColumns.GetValueOrDefault(SelectGapKey(gap), -1);
#else
        return _gapColumns.TryGetValue(SelectGapKey(gap), out var result) ? result : -1;
#endif
    }
}


internal class Locator<T>(Func<string, string, T> factory)
    where T : class, ILocatable
{
    private readonly List<T> _dataList = [];
    private readonly Index<T> _indexer = new("_ROOT_NODE_", "_ROOT_");

    /// <summary>
    ///     创建一个新的对象并返回，不添加到当前集合中。
    /// </summary>
    /// <returns></returns>
    public T Create(string key, string value)
    {
        return factory(key, value);
    }

    /// <summary>
    ///     将给定元素添加到当前的集合中并锁定其路径。
    /// </summary>
    /// <param name="item">待添加的元素</param>
    public void Add(T item)
    {
        item.Lock();
        var node = _indexer.GetChild(item.GetLocation());
        node.Data = item;
        _dataList.Add(item);
    }

    /// <summary>
    ///     查找给定路径的已保存数据。
    /// </summary>
    /// <param name="path">给定路径，允许比已有路径更多。</param>
    /// <returns>如果未找到符合给定路径的数据，将返回null</returns>
    public T? Find(IEnumerable<KeyValuePair<string, string>> path)
    {
        return _indexer.FindChild(path, false)?.Data;
    }

    /// <summary>
    ///     获取所有已经存储的数据。
    /// </summary>
    public IReadOnlyList<T> GetDataList()
    {
        return _dataList;
    }


    /// <summary>
    ///     清空已读取的数据
    /// </summary>
    public void Clear()
    {
        _indexer.Clear();
        _dataList.Clear();
    }
}